# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.


include	"bspln.h"
define	ILLEGAL_ORDER	2	#error returns
define	NO_DEG_FREEDOM	3


.help splsqv 2 "IRAF Math Library"

Adapted from L2APPR, * A Practical Guide To Splines *  by C. DeBoor.
Eliminated data entry via the common /data/ (D.Tody, 4-july-82).  Calls
subprograms  BSPLVB, BCHFAC, BCHSLV.

SPLSQV constructs the (weighted discrete) l2-approximation by splines of
order K with knot sequence  T[1], ..., t[nt+k]  to given data points
(TAU[i], GTAU[i]), i=1,...,ntau.  The b-spline coefficients BCOEF of the
approximating spline are determined from the normal equations using
Cholesky'smethod.

SPLSQV (tau, gtau, weight, ntau, t, nt, k, work, diag, bcoef, ier)


INPUT -----

ntau		Number of data points
tau[ntau]	x-value of the data points.
gtau[ntau]	y-value of the data points.
weight[ntau]	The corresponding weights.
t[nt]		The knot sequence
nt		The dimension of the space of splines of order k with knots t.
k		The order of the b-spline to be fitted.


WORK ARRAYS -----

work[k,nt]	A work array of size (at least) K*NT. its first K rows are used
		for the K lower diagonals of the gramian matrix C.
diag[nt]	A work array of length NT used in BCHFAC.


OUTPUT -----

bcoef[nt]	The b-spline coefficients of the l2-approximation.
ier		Error code: zero if ok, else small integer error code.


METHOD -----

The b-spline coefficients of the l2-appr. are determined as the solution
of the normal equations

	sum ((B[i],B[j]) * BCOEF[j] : j=1:nt) = (B[i],G), 	i = 1 to nt.

where, B[i] denotes the i-th b-spline, G denotes the function to be
approximated, and the INNER PRODUCT of two functions F and G is given by

	(F,G)  :=  sum (F(TAU[i]) * G(TAU[i]) * WEIGHT[i] : i=1:ntau).

The relevant function values of the b-splines  B[i], i=1:nt, are supplied
by the subprogram BSPLVB.  The coeff. matrix C, with

	C(i,j) :=  (B[i], B[j]), i,j=1:n,

of the normal equations is symmetric and (2*k-1)-banded, therefore can be
specified by giving its k bands at or below the diagonal. for i=1,...,n,
we store

	(B[i],B[j])  =  B[i,j]  in  WORK[i-j+1,j], j=i,...,min0(i+k-1,nt)

and the right side

	(B[i], G)  in  BCOEF[i].

since b-spline values are most efficiently generated by finding simultaneously
the value of EVERY nonzero b-spline at one point, the entries of C (i.e., of
WORK), are generated by computing, for each ll, all the terms involving
TAU(ll) simultaneously and adding them to all relevant entries.
.endhelp _______________________________________________________________


procedure splsqv (tau, gtau, weight, ntau, t, nt, k, work, diag, bcoef, ier)

real	tau[ntau], gtau[ntau], weight[ntau]
real	t[nt], work[k,nt], diag[nt], bcoef[nt]
int	ntau, nt, k, ier
int	i, j, jj, left, leftmk, ll, mm
real	biatx[KMAX], dw

begin
	ier = 0
	if (k < 1 || k > KMAX) {
	    ier = ILLEGAL_ORDER
	    return
	}
	if (nt <= k || nt >= ntau+k) {
	    ier = NO_DEG_FREEDOM
	    return
	}

	do j = 1, nt {
	    bcoef[j] = 0.
	    do i = 1, k
		work[i,j] = 0.
	}

	left = k
	leftmk = 0

	do ll = 1, ntau {
	    while (left != nt) {
		if (tau[ll] < t[left+1])
		    break
		left = left + 1
		leftmk = leftmk + 1
	    }

	    call bsplvb (t, k, 1, tau[ll], left, biatx)

#   BIATX[mm] contains the value of B[left-k+mm] at TAU[ll].
#   hence, with DW := BIATX[mm] * WEIGHT[ll], the number DW * GTAU[ll]
#   is a summand in the inner product
#      (B[left-k+mm],G)  which goes into  BCOEF[left-k+mm]
#   and the number BIATX[jj] * dw is a summand in the inner product
#      (B[left-k+jj],  B[left-k+mm]),  into  WORK[jj-mm+1,left-k+mm]
#   since (left-k+jj) - (left-k+mm) + 1 = jj - mm + 1.

	    do mm = 1, k {
		dw = biatx[mm] * weight[ll]
		j = leftmk + mm
		bcoef[j] = dw * gtau[ll] + bcoef[j]
		i = 1

		do jj = mm, k {
		    work[i,j] = biatx[jj] * dw + work[i,j]
		    i = i + 1
		}
	    }
	}

#  construct cholesky factorization for C in WORK,  then use it to solve
#  the normal equations
#       c * x = bcoef
#  for X, and store X in BCOEF.

	call bchfac (work, k, nt, diag)
	call bchslv (work, k, nt, bcoef)
	return
end
